export type CallerFunc<T> = () => Promise<T>

type TaskQueueFunc = (del: boolean) => void

/**
 * 进行扩展，带有id可以进行排序
 */
export interface TaskQueueItem {
  id: string;
  func: TaskQueueFunc;
  del: boolean;
}

/**
 * promise数量控制
 */
export default class LimitPromise<T> {
  private readonly _max: number = 0;
  private _count: number = 0;
  private _taskQueue: TaskQueueItem[] = [];

  constructor(max?: number) {
    this._max = max || 3
  }

  /**
   * 调用器
   * @param caller 异步任务函数，它必须是async函数或者返回Promise的函数
   * @param id 可以用于给任务排序
   */
  call<T>(caller: CallerFunc<T>, id?: string): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      const task = this._createTask(caller, resolve, reject)
      if (this._count >= this._max) {
        this._taskQueue.push({
          func: task,
          id: id || Date.now() + '' + this._taskQueue.length,
          del: false,
        });
      } else {
        task(false);
      }
    })
  }


  /**
   * 给id这个任务进行提前
   * @param id
   */
  toFirstNest(id: string) {
    let number = this._taskQueue.findIndex(i => i.id === id);
    if (number > -1) {
      let del = this._taskQueue.splice(number, 1)[0];
      if (del) {
        // 放到第一个
        this._taskQueue.unshift(del)
        return true
      }
    }
    return false
  }

  delTask(id: string) {
    let number = this._taskQueue.findIndex(i => i.id === id);
    if (number > -1) {
      let del = this._taskQueue[number]
      del.del = true
      return del
    }
    return null
  }

  /**
   * 创建一个任务
   * @param caller
   * @param resolve
   * @param reject
   * @private
   */
  private _createTask<T>(caller: CallerFunc<T>, resolve, reject): TaskQueueFunc {
    return (del: boolean) => {
      let fin = () => {
        // 任务队列的消费区，利用Promise的finally方法，在异步任务结束后，取出下一个任务执行
        this._count--;
        if (this._taskQueue.length) {
          const task = this._taskQueue.shift();
          task.func(task.del);
        }
      }
      if (del) {
        reject('LimitPromise任务删除')
        fin()
      } else {
        caller().then(resolve).catch(reject).finally(fin)
      }
      this._count++;
    }
  }
}

/**
 const limitPromise = new LimitPromise(1) // 限制这里可以改
 function call(args) { // 若依系统里面request文件包装一下 外面不变，这样就是限制成每次就一个请求
  return limitPromise.call(() => service(args))
}
 */
